package com.cmes.mes.audit.service;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cmes.core.constant.enums.EnableStatus;
import com.cmes.core.context.security.SecurityContextHolder;
import com.cmes.core.utils.DateUtils;
import com.cmes.core.utils.Preconditions;
import com.cmes.jdbc.repository.GenericRepository;
import com.cmes.jdbc.service.GenericService;
import com.cmes.mes.audit.constant.AuditOperationEnum;
import com.cmes.mes.audit.constant.AuditStatusEnum;
import com.cmes.mes.audit.constant.BusinessEnum;
import com.cmes.mes.audit.constant.ErrorCode;
import com.cmes.mes.audit.entity.AuditModuleEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 通用业务操作类
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
public abstract class GenericBusinessService<S extends GenericService<R, E>, R extends GenericRepository<E>, E extends AuditModuleEntity> {

    @Autowired
    private AuditLogService auditLogService;
    @Autowired
    protected S genericService;

    protected AuditLogService getAuditLogService() {
        return auditLogService;
    }

    protected S getGenericService() {
        return genericService;
    }

    /**
     * 获取当前业务
     */
    abstract protected BusinessEnum getBusiness();

    // region 增删查改

    /**
     * 新增前校验
     */
    public void canAdd(E entity) {}

    /**
     * 新增
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Long add(E entity) {
        beforeAdd(entity);
        Long id = executeAdd(entity);
        afterAdd(entity);
        return id;
    }

    /**
     * 执行新增操作
     */
    protected Long executeAdd(E entity) {
        getGenericService().insert(entity);
        return entity.getId();
    }

    /**
     * 新增前操作
     */
    protected void beforeAdd(E entity) {
        entity.setId(IdWorker.getId());
        initAuditProperty(entity);
    }

    /**
     * 新增后操作
     */
    protected void afterAdd(E entity) {}

    /**
     * 初始化审核相关字段
     */
    protected void initAuditProperty(E entity) {
        if (getBusiness().getAudit()) {
            entity.setAuditStatus(AuditStatusEnum.DRAFT.getCode());
        } else {
            entity.setAuditStatus(AuditStatusEnum.AUDITED.getCode());
        }
        entity.setStatus(EnableStatus.ENABLE.getCode());
        entity.setAuditTime(DateUtils.now());
        entity.setAuditBy(SecurityContextHolder.getCurrentUserName());
    }

    /**
     * 修改前校验
     */
    public E canUpdate(E entity) {
        E db = checkExist(entity.getId());
        if (getBusiness().getAudit()) {
            Preconditions.check(() -> !AuditStatusEnum.DRAFT.match(db.getAuditStatus()), ErrorCode.UPDATE_NOT_DRAFT, getBusiness().getName());
        }
        Preconditions.check(() -> !EnableStatus.ENABLE.match(db.getStatus()), ErrorCode.UPDATE_NOT_ENABLE, getBusiness().getName());
        return db;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Long update(E entity, E db) {
        beforeUpdate(entity, db);
        getGenericService().update(entity);
        afterUpdate(entity, db);
        return entity.getId();
    }

    /**
     * 修改前操作
     */
    protected void beforeUpdate(E entity, E db) {}

    /**
     * 修改后操作
     */
    protected void afterUpdate(E entity, E db) {}

    /**
     * 删除前校验
     */
    public List<E> canDelete(List<Long> ids) {
        List<E> dbs = checkExist(ids);
        if (getBusiness().getAudit()) {
            checkAuditStatus(dbs, AuditStatusEnum.DRAFT, "删除");
        }
        return dbs;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void delete(List<Long> ids, List<E> dbs) {
        beforeDelete(ids, dbs);
        getGenericService().deleteBatch(ids);
        afterDelete(ids, dbs);
    }

    /**
     * 删除前操作
     */
    protected void beforeDelete(List<Long> ids, List<E> dbs) {}

    /**
     * 删除后操作
     */
    protected void afterDelete(List<Long> ids, List<E> dbs) {}

    // endregion

    // region 流程操作

    /**
     * 能否提交
     */
    public List<E> canSubmit(List<Long> ids) {
        checkAudit();
        List<E> dbs = checkExist(ids);
        checkAuditStatus(dbs, AuditOperationEnum.SUBMIT, AuditStatusEnum.DRAFT);
        return dbs;
    }

    /**
     * 批量提交
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void submit(List<Long> ids, List<E> entities, String cause) {
        getAuditLogService().submit(getBusiness().getCode(), entities, cause);
        changeAuditStatus(ids, AuditStatusEnum.DRAFT, AuditStatusEnum.SUBMIT);
    }

    /**
     * 能否驳回
     */
    public List<E> canReject(List<Long> ids) {
        checkAudit();
        List<E> dbs = checkExist(ids);
        checkAuditStatus(dbs, AuditOperationEnum.REJECT, AuditStatusEnum.SUBMIT);
        return dbs;
    }

    /**
     * 驳回
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void reject(List<Long> ids, List<E> entities, String cause) {
        getAuditLogService().reject(getBusiness().getCode(), entities, cause);
        changeAuditStatus(ids, AuditStatusEnum.SUBMIT, AuditStatusEnum.DRAFT);
    }

    /**
     * 能否审核通过
     */
    public List<E> canAudit(List<Long> ids) {
        checkAudit();
        List<E> dbs = checkExist(ids);
        checkAuditStatus(dbs, AuditOperationEnum.AUDITED, AuditStatusEnum.SUBMIT);
        return dbs;
    }

    /**
     * 审核通过
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void audit(List<Long> ids, List<E> entities, String cause) {
        getAuditLogService().audit(getBusiness().getCode(), entities, cause);
        changeAuditStatus(ids, AuditStatusEnum.SUBMIT, AuditStatusEnum.AUDITED);
        afterAudit(ids, entities);
    }

    /**
     * 审核通过后操作
     */
    protected void afterAudit(List<Long> ids, List<E> entities) {}

    /**
     * 能否通过驳回
     */
    public List<E> canReAudit(List<Long> ids) {
        checkAudit();
        List<E> dbs = checkExist(ids);
        checkAuditStatus(dbs, AuditOperationEnum.RE_AUDITED, AuditStatusEnum.AUDITED);
        return dbs;
    }

    /**
     * 通过驳回
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void reAudit(List<Long> ids, List<E> entities, String cause) {
        getAuditLogService().reAudit(getBusiness().getCode(), entities, cause);
        changeAuditStatus(ids, AuditStatusEnum.AUDITED, AuditStatusEnum.DRAFT);
        afterReAudit(ids, entities);
    }

    /**
     * 通过驳回后操作
     */
    protected void afterReAudit(List<Long> ids, List<E> entities) {}

    /**
     * 能否停用
     */
    public List<E> canDisable(List<Long> ids) {
        List<E> dbs = checkExist(ids);
        Set<String> auditStatusCodes = new HashSet<>();
        Set<String> statusCodes = new HashSet<>();
        dbs.forEach(v -> {
            if (!AuditStatusEnum.AUDITED.match(v.getAuditStatus())) {
                auditStatusCodes.add(v.getSn());
            }
            if (!EnableStatus.ENABLE.match(v.getStatus())) {
                statusCodes.add(v.getSn());
            }
        });
        if (getBusiness().getAudit()) {
            Preconditions.notEmpty(auditStatusCodes, ErrorCode.AUDIT_STATUS_FAIL, getBusiness().getName(), auditStatusCodes, AuditStatusEnum.AUDITED.getValue(), AuditOperationEnum.DISABLE.getValue());
        }
        Preconditions.notEmpty(statusCodes, ErrorCode.DISABLE_STATUS_FAIL, statusCodes);
        return dbs;
    }

    /**
     * 作废
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void disable(List<Long> ids, List<E> entities, String cause) {
        getAuditLogService().disable(getBusiness().getCode(), entities, cause);
        getGenericService().update(Wrappers.lambdaUpdate(getGenericService().currentEntityClass())
                .set(E::getStatus, EnableStatus.DISABLE.getCode())
                .set(E::getAuditBy, SecurityContextHolder.getCurrentUserName())
                .set(E::getAuditTime, DateUtils.now())
                .in(E::getId, ids)
                .eq(E::getStatus, EnableStatus.ENABLE.getCode())
                .eq(E::getAuditStatus, AuditStatusEnum.AUDITED.getCode()));
        afterDisable(ids, entities);
    }

    /**
     * 停用后操作
     */
    protected void afterDisable(List<Long> ids, List<E> entities) {}

    /**
     * 修改审核状态
     */
    private void changeAuditStatus(List<Long> ids, AuditStatusEnum fromStatus, AuditStatusEnum toStatus) {
        getGenericService().update(Wrappers.lambdaUpdate(getGenericService().currentEntityClass())
                .set(E::getAuditStatus, toStatus.getCode())
                .set(E::getAuditBy, SecurityContextHolder.getCurrentRealName())
                .set(E::getAuditTime, DateUtils.now())
                .in(E::getId, ids)
                .eq(E::getAuditStatus, fromStatus.getCode()));
    }

    private void checkAudit() {
        Preconditions.isFalse(getBusiness().getAudit(), ErrorCode.NOT_SUPPORT, getBusiness().getAudit());
    }

    // endregion

    /**
     * 检查选择的行是否全部存在
     */
    protected E checkExist(Long id) {
        E db = getGenericService().getById(id);
        Preconditions.isNull(db, ErrorCode.DATA_NOT_EXIST, getBusiness().getName());
        return db;
    }

    /**
     * 检查选择的行是否全部存在
     */
    protected List<E> checkExist(List<Long> ids) {
        List<E> dbs = getGenericService().listByIds(ids);
        Preconditions.isEmpty(dbs, ErrorCode.DATA_NOT_EXIST, getBusiness().getName());
        Preconditions.check(() -> dbs.size() != ids.size(), ErrorCode.DATA_NOT_EXIST, getBusiness().getName());
        return dbs;
    }

    /**
     * 检查选择的数据审核状态是否符合条件
     */
    protected void checkAuditStatus(List<E> dbs, AuditOperationEnum operation, AuditStatusEnum status) {
        checkAuditStatus(dbs, status, operation.getValue());
    }

    /**
     * 检查选择的数据审核状态是否符合条件
     */
    protected void checkAuditStatus(List<E> dbs, AuditStatusEnum status, String operate) {
        Set<String> codes = dbs.stream()
                .filter(v -> !status.match(v.getAuditStatus()))
                .map(E::getSn)
                .collect(Collectors.toSet());
        Preconditions.notEmpty(codes, ErrorCode.AUDIT_STATUS_FAIL, getBusiness().getName(), codes, status.getValue(), operate);
    }

}
